/*******************************************************************************
 * Copyright (c) 2000, 2003 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Common Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
module dwt.ole.win32.oleeventsink;


private import dwt.dwt;


private import dwt.ole.win32.ole;
private import dwt.ole.win32.oleclientsite;
private import dwt.ole.win32.olecontrolsite;
private import dwt.ole.win32.olemisc;
private import dwt.ole.win32.variant;
private import dwt.internal.ole.win32.com;
private import dwt.internal.win32.wintypes;
private import dwt.internal.ole.win32.OAIDL;
private import dwt.internal.ole.win32.extras;
private import dwt.util.eventhandler;

private import dwt.internal.ole.win32.ifs;



private class _DispatchImpl : IDispatch {

	private import std.c.windows.windows;
	private import std.c.windows.com;


	OleEventSink parent;
	this(OleEventSink sink) { parent = sink;}
extern (Windows) :
	HRESULT QueryInterface(REFIID riid, void ** ppvObject){
		return parent.QueryInterface(riid, ppvObject);
	}
	ULONG AddRef()	{ return parent.AddRef(); }
	ULONG Release()	{ return parent.Release(); }
	HRESULT GetTypeInfoCount(UINT * pctinfo) { return COM.E_NOTIMPL; }
	HRESULT GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo * ppTInfo) { return COM.E_NOTIMPL; }
	HRESULT GetIDsOfNames(REFIID riid, LPOLESTR * rgszNames, UINT cNames, LCID lcid, DISPID * rgDispId) { return COM.E_NOTIMPL; }
	HRESULT Invoke(DISPID dispIdMember,REFIID riid,LCID lcid,WORD wFlags,DISPPARAMS* pDispParams,VARIANT* pVarResult,EXCEPINFO* pExcepInfo,UINT* puArgErr){
		return parent.Invoke(dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
	}
}




class OleEventSink
{

	private import std.c.windows.windows;
	private import std.c.windows.com;


	private OleControlSite widget;

	private _DispatchImpl iDispatch;

	private IUnknown objIUnknown;
	private int  eventCookie;
	private GUID* eventGuid;

	private OleEventTable eventTable;

	private int refCount = 0;

this(OleControlSite widget, IUnknown iUnknown, GUID* riid) {

	assert(iUnknown);

	this.widget = widget;
	this.eventGuid = riid;
	this.objIUnknown = (iUnknown);

	createCOMInterfaces();
}

HRESULT QueryInterface(REFIID riid, void ** ppvObject){
	if (riid is null || ppvObject is null)
		return COM.E_INVALIDARG;

	if ( COM.IsEqualGUID(riid, &COM.IIDIUnknown) || COM.IsEqualGUID(riid, &COM.IIDIDispatch) ||
			COM.IsEqualGUID(riid, eventGuid)) {
		*ppvObject = cast(void*)iDispatch;
		AddRef();
		return OLE.S_OK;
	}

	*ppvObject = null;
	return COM.E_NOINTERFACE;
}
ULONG AddRef(){
	return OS.InterlockedIncrement(&refCount);
}

ULONG Release(){
	int ref1 = OS.InterlockedDecrement(&refCount);
	if(ref1 == 0)
		disposeCOMInterfaces();
	return ref1;
}

void connect () {
	IConnectionPointContainer cpc;
	if (objIUnknown.QueryInterface(&COM.IIDIConnectionPointContainer, cast(void**)&cpc) == COM.S_OK) {
		IConnectionPoint cp;
		if (cpc.FindConnectionPoint(eventGuid, &cp) == COM.S_OK) {
			uint Cookie;
			if (cp.Advise(iDispatch, &Cookie) == COM.S_OK)
				eventCookie = Cookie;
			cp.Release();
		}
		cpc.Release();
	}
}
protected void createCOMInterfaces() {
	iDispatch = new _DispatchImpl(this);
}
void addListener(int eventID, OleListener listener) {
	if (listener is null) OLE.error (__FILE__, __LINE__, DWT.ERROR_NULL_ARGUMENT);
	if (eventTable is null) eventTable = new OleEventTable ();
	eventTable.hook(eventID, listener);
}

void disconnect() {
	// disconnect event sink
	if (eventCookie != 0 && objIUnknown !is null) {
		IConnectionPointContainer cpc;
		if (objIUnknown.QueryInterface(&COM.IIDIConnectionPointContainer, cast(void**)&cpc) == COM.S_OK) {
			IConnectionPoint cp;
			if (cpc.FindConnectionPoint(eventGuid, &cp) == COM.S_OK) {
				if (cp.Unadvise(eventCookie) == COM.S_OK) {
					eventCookie = 0;
				}
				cp.Release();
			}
			cpc.Release();
		}
	}
}
protected void disposeCOMInterfaces() {
	iDispatch = null;
}

HRESULT Invoke(DISPID dispIdMember,REFIID riid,LCID lcid,WORD wFlags,DISPPARAMS* pDispParams,VARIANT* pVarResult,EXCEPINFO* pExcepInfo,UINT* puArgErr)
{
	if (eventTable is null || !eventTable.hooks(dispIdMember)) return COM.S_OK;

	// Construct an array of the parameters that are passed in
	// Note: parameters are passed in reverse order - here we will correct the order
	Variant[] eventInfo = null;
	if (pDispParams !is null) {
		eventInfo = new Variant[pDispParams.cArgs];

		int count = pDispParams.cArgs;
		for (int j = 0; j < count; j++){
			eventInfo[j].setData(&pDispParams.rgvarg[count - 1 - j]);
		}
	}

	OleEvent event = new OleEvent();
	event.arguments = eventInfo;
	try {
		notifyListener(dispIdMember,event);
	} catch (Exception t) {
	}

	return COM.S_OK;
}
/**
* Notify listeners of an event.
* <p>
*	This method notifies all listeners that an event
* has occurred.
*
* @param eventType the desired DWT event
* @param event the event data
*
* @exception SWTError <ul>
*		<li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
*		<li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
* 		<li>ERROR_NULL_ARGUMENT when handler is null</li>
*	</ul>
*/
private void notifyListener (int eventType, OleEvent event) {
	if (event is null) OLE.error (__FILE__, __LINE__, DWT.ERROR_NULL_ARGUMENT);
	if (eventTable is null) return;
	event.type = eventType;
	event.widget = widget;
	eventTable.sendEvent (event);
}

void removeListener(int eventID, OleListener listener) {
	if (listener is null) OLE.error (__FILE__, __LINE__, DWT.ERROR_NULL_ARGUMENT);
	if (eventTable is null) return;
	eventTable.unhook (eventID, listener);
}

boolean hasListeners() {
	return eventTable.hasEntries();
}

/**
 * <Shawn> delegate support,
 * basic
 */
void handleEvent(TypedHandler handler) {
	assert(handler);
	if (eventTable is null) eventTable = new OleEventTable ();
	eventTable.hook(handler);
}

/**
 * <Shawn> delegate support,
 * handle a general event with a OleEvent parameters delegate
 */
// handle a general event
void handleEvent(Object customData, int eventType, void delegate(OleEvent) func){
	handleEvent(new OleEventHandler(customData, eventType, func));
}
// Array as argument
/* void handleEvent(Object[] customDatas, int eventType, void delegate(OleEvent) func){
	handleEvent(new OleEventHandler(customDatas, eventType, func));
} */

// basic unhandle
void unhandleEvent(TypedHandler handler) {
	assert(handler);
	if (eventTable is null) return;
	eventTable.unhook(handler);
}

public void unhandleEvent(int eventType, void delegate(OleEvent) func){
	TypedHandler handler = new OleEventHandler(null, eventType, func);
	unhandleEvent(handler);
}


}
